﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using FileService;
using Go;
using GoRpc;

namespace LoadFile
{
    public partial class MainForm : Form
    {
        class NodeTag
        {
            public bool isFile;
            public string name;
            public generator.child action;
        }

        abstract class IoBase
        {
        }

        class CreateFile : IoBase
        {
            public wait_group waitCreateDir;
            public wait_group notifyIo;
            public string srcFile;
            public string dstFile;
        }

        class CreateDir : IoBase
        {
            public wait_group waitCreateDir;
            public wait_group notifyCreateDir;
            public wait_group notifyIo;
            public string dir;
        }

        class SetDirTime : IoBase
        {
            public wait_group waitIo;
            public string dir;
            public DateTime[] time;
        }

        control_strand strand;
        generator _action;
        chan<tuple<TreeNode, bool>> expandChan;
        chan<tuple<string, string>> fileChan;
        chan<tuple<string, string>> dirChan;
        chan<bool> testChan;
        chan<tuple<TreeNode, string[]>> dragChan;

        public MainForm()
        {
            system_tick.high_resolution();
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            textBox_ip.Focus();
            strand = new control_strand(this);
            expandChan = chan<tuple<TreeNode, bool>>.make(strand, -1);
            fileChan = chan<tuple<string, string>>.make(strand, -1);
            dirChan = chan<tuple<string, string>>.make(strand, -1);
            testChan = chan<bool>.make(strand, -1);
            dragChan = chan<tuple<TreeNode, string[]>>.make(strand, -1);
            generator.go(out _action, strand, () => Action(null), delegate ()
            {
                treeView1.Nodes.Clear();
                Reset();
            });
        }

        private void button_ip_Click(object sender, EventArgs e)
        {
            _action.stop();
        }

        private void numericUpDown_block_ValueChanged(object sender, EventArgs e)
        {
            int block = (-4) & ((int)numericUpDown_block.Value + 3);
            if (block != numericUpDown_block.Value)
            {
                numericUpDown_block.Value = block;
            }
        }

        private void Reset()
        {
            generator.go(out _action, strand, () => Action(textBox_ip.Text), delegate ()
            {
                treeView1.Nodes.Clear();
                Reset();
            });
        }

        static void GetFullPath(StringBuilder builder, TreeNode node)
        {
            NodeTag tag = (NodeTag)node.Tag;
            if (node.Parent != null)
            {
                GetFullPath(builder, node.Parent);
                builder.Append('\\');
            }
            builder.Append(tag.name);
        }

        StringBuilder builder = new StringBuilder(256);
        static string[] sizeFmt = new string[] { "0 B", "0.00 KB", "0.00 MB", "0.00 GB", "0.00 TB" };
        static private string SizeFormat(long size)
        {
            for (int k = sizeFmt.Length - 1; k >= 0; k--)
            {
                if (size >= (1L << (k * 10)))
                {
                    return ((double)size / (1L << (k * 10))).ToString(sizeFmt[k]);
                }
            }
            return "0 B";
        }

        private async Task EnumTree(Rpc.Session session, TreeNode tree)
        {
            builder.Clear();
            GetFullPath(builder, tree);
            string path = builder.ToString();
            var dirs = await session["IO.GetDirectories"].Call<string[]>(path);
            if (dirs.Success)
            {
                for (int j = 0; j < dirs.result.Length; j++)
                {
                    TreeNode newNode = tree.Nodes.Add(dirs.result[j]);
                    newNode.Tag = new NodeTag { isFile = false, name = dirs.result[j] };
                    newNode.Nodes.Add($".\\").Tag = new NodeTag { isFile = false };
                    await generator.sleep(1);
                }
            }
            var files = await session["IO.GetFiles"].Call<string[]>(path);
            if (files.Success)
            {
                for (int j = 0; j < files.result.Length; j++)
                {
                    var size = await session["IO.FileSize"].Call<long>($"{path}\\{files.result[j]}");
                    TreeNode newNode = tree.Nodes.Add($"{files.result[j]}  {SizeFormat(size.result)}");
                    newNode.Tag = new NodeTag { isFile = true, name = files.result[j] };
                }
            }
        }

        private async Task StopEnumAction(generator.children enumChild, TreeNode tree)
        {
            if (null != tree.Tag)
            {
                NodeTag tag = (NodeTag)tree.Tag;
                if (null != tag.action)
                {
                    await enumChild.stop(tag.action);
                    tag.action = null;
                }
                foreach (TreeNode item in tree.Nodes)
                {
                    await StopEnumAction(enumChild, item);
                }
            }
        }

        private async Task<bool> ReadFile(Rpc.Session session, int block, long fileSize, string srcFile, string dstFile, bool force, shared_strand ioStrand, chan<long> postWrite)
        {
            if (!force && File.Exists(dstFile))
            {
                textBox_msg.AppendText($"{dstFile} 已存在\r\n");
                return false;
            }
            Rpc.Result<bool> result = await session["IO.ReadFile"].Call<bool>(srcFile, session.OverCallback(async delegate (DateTime[] fileInfo)
            {
                Rpc.OverRecv[] recvs = Rpc.Session.OverRecv();
                FileStream file = null;
                bool completed = false;
                try
                {
                    generator.lock_stop();
                    await generator.send_strand(ioStrand, delegate ()
                    {
                        try
                        {
                            if (File.Exists(dstFile))
                            {
                                if (File.GetLastWriteTime(dstFile) >= fileInfo[0])
                                {
                                    postWrite.post(fileSize);
                                    strand.post(() => textBox_msg.AppendText($"{dstFile} 忽略\r\n"));
                                    completed = true;
                                    return;
                                }
                                else
                                {
                                    strand.post(() => textBox_msg.AppendText($"{dstFile} 覆盖\r\n"));
                                }
                            }
                            file = File.Open(dstFile, FileMode.Create);
                        }
                        catch (Exception) { }
                    });
                    generator.unlock_stop();
                    if (null == file)
                    {
                        return completed;
                    }
                    if (null != recvs)
                    {
                        Rpc.RecvDoubleBuffer dbuff = new Rpc.RecvDoubleBuffer(block * 1024, ioStrand);
                        bool first = true;
                        long offset = 0;
                        for (offset = 0; offset < recvs[0].Length;)
                        {
                            int len = await recvs[0].Recv(dbuff.RecvBuff, 0, dbuff.RecvBuff.Length);
                            if (len <= 0)
                            {
                                break;
                            }
                            await dbuff.Exchange();
                            if (dbuff.Error)
                            {
                                break;
                            }
                            dbuff.Async(delegate ()
                            {
                                if (first)
                                {
                                    first = false;
                                    if (0 == string.Compare(new DriveInfo(dstFile).DriveFormat, "NTFS", true))
                                    {
                                        file.SetLength(recvs[0].Length);
                                    }
                                }
                                file.Write(dbuff.WriteData, 0, len);
                            });
                            offset += len;
                            postWrite.post(len);
                        }
                        await dbuff.Exchange();
                        if (!dbuff.Error)
                        {
                            completed = offset == recvs[0].Length;
                        }
                        return true;
                    }
                    else
                    {
                        completed = true;
                        return true;
                    }
                }
                finally
                {
                    generator.lock_stop();
                    await generator.send_strand(ioStrand, delegate ()
                    {
                        if (null != file)
                        {
                            try
                            {
                                file.Dispose();
                                if (completed)
                                {
                                    File.SetLastWriteTime(dstFile, fileInfo[0]);
                                    File.SetCreationTime(dstFile, fileInfo[1]);
                                    File.SetLastAccessTime(dstFile, fileInfo[2]);
                                }
                                else
                                {
                                    File.Delete(dstFile);
                                }
                            }
                            catch (Exception) { }
                        }
                    });
                    generator.unlock_stop();
                }
            }));
            if (result.Success)
            {
                return result.result;
            }
            return false;
        }

        private async Task<bool> WriteFile(Rpc.Session session, int block, string srcFile, string dstFile, bool force, shared_strand ioStrand, chan<long> postWrite)
        {
            FileStream file = null;
            DateTime[] info = null;
            try
            {
                generator.lock_stop();
                await generator.send_strand(ioStrand, delegate ()
                {
                    try
                    {
                        file = File.OpenRead(srcFile);
                        info = new DateTime[]
                        {
                            File.GetLastWriteTime(srcFile),
                            File.GetCreationTime(srcFile),
                            File.GetLastAccessTime(srcFile),
                        };
                    }
                    catch (Exception) { }
                });
                generator.unlock_stop();
                if (null == file)
                {
                    return false;
                }
                long sendCount = 0;
                long fileLength = file.Length;
                if (fileLength > 0)
                {
                    Rpc.SendDoubleBuffer dbuff = new Rpc.SendDoubleBuffer(block * 1024);
                    dbuff.Async(() => file.Read(dbuff.ReadBuff, 0, (int)Math.Min(dbuff.ReadBuff.Length, fileLength)));
                    session.OverSend(fileLength, async delegate (long offset, long count)
                    {
                        await dbuff.Exchange();
                        if (dbuff.Error)
                        {
                            return;
                        }
                        var data = dbuff.SendData;
                        if (count - data.Count > 0)
                        {
                            dbuff.Async(() => file.Read(dbuff.ReadBuff, 0, (int)Math.Min(dbuff.ReadBuff.Length, count - data.Count)));
                        }
                        sendCount += data.Count;
                        postWrite.post(data.Count);
                        await Rpc.Session.Send(data);
                    });
                }
                Rpc.Result<bool> result = await session["IO.WriteFile"].Call<bool>(force, dstFile, info);
                if (result.Success)
                {
                    postWrite.post(fileLength - sendCount);
                    return result.result;
                }
                return false;
            }
            catch (generator.stop_exception)
            {
                throw;
            }
            finally
            {
                file?.Dispose();
            }
        }

        private async Task<bool> ReadFileMT(Rpc.Session session, long[] sessToken, string srcFile, string dstFile, bool force, shared_strand ioStrand, chan<float> progress)
        {
            if (!force && File.Exists(dstFile))
            {
                textBox_msg.AppendText($"{dstFile} 已存在\r\n");
                return false;
            }
            Rpc.Result<DateTime[]> fileInfo = await session["IO.FileInfo"].Call<DateTime[]>(srcFile);
            if (!fileInfo.Success)
            {
                return false;
            }
            bool completed = false;
            FileStream file = null;
            try
            {
                generator.lock_stop();
                await generator.send_strand(ioStrand, delegate ()
                {
                    try
                    {
                        if (File.Exists(dstFile))
                        {
                            if (File.GetLastWriteTime(dstFile) >= fileInfo.result[1])
                            {
                                strand.post(() => textBox_msg.AppendText($"{dstFile} 忽略\r\n"));
                                completed = true;
                                return;
                            }
                            else
                            {
                                strand.post(() => textBox_msg.AppendText($"{dstFile} 覆盖\r\n"));
                            }
                        }
                        file = File.Open(dstFile, FileMode.Create);
                    }
                    catch (Exception) { }
                });
                generator.unlock_stop();
                if (null == file)
                {
                    return completed;
                }
                wait_group recvWg = new wait_group();
                TransmitFile.RecvResult recvResult = new TransmitFile.RecvResult();
                tuple<Action, Delegate> recvHandler = TransmitFile.RecvHandler(file, recvResult, recvWg, ioStrand, progress);
                try
                {
                    Rpc.Result<bool> result = await session["IO.ReadFileMT"].Call<bool>(sessToken, srcFile, session.OverCallback(recvHandler.value2));
                    if (!result.Success)
                    {
                        return false;
                    }
                    if (0 == recvResult.fileLength)
                    {
                        progress.post(1f);
                    }
                    completed = recvResult.fileLength == recvResult.recvLength;
                    return completed;
                }
                finally
                {
                    generator.lock_stop();
                    recvHandler.value1();
                    await recvWg.wait();
                    generator.unlock_stop();
                }
            }
            finally
            {
                generator.lock_stop();
                await generator.send_strand(ioStrand, delegate ()
                {
                    if (null != file)
                    {
                        try
                        {
                            file.Dispose();
                            if (completed)
                            {
                                File.SetLastWriteTime(dstFile, fileInfo.result[0]);
                                File.SetCreationTime(dstFile, fileInfo.result[1]);
                                File.SetLastAccessTime(dstFile, fileInfo.result[2]);
                            }
                            else
                            {
                                File.Delete(dstFile);
                            }
                        }
                        catch (Exception) { }
                    }
                });
                generator.unlock_stop();
            }
        }

        private async Task<bool> WriteFileMT(Rpc.Session session, int block, long[] sessToken, Rpc.Session[] sessions, string path, string dstFile, bool force, shared_strand ioStrand, chan<float> progress)
        {
            DateTime[] fileInfo = await generator.send_strand(ioStrand, delegate ()
            {
                return new DateTime[]
                {
                    File.GetLastWriteTime(path),
                    File.GetCreationTime(path),
                    File.GetLastAccessTime(path),
                };
            });
            Rpc.Result<bool> result = await session["IO.WriteFileMT"].Call<bool>(sessToken, force, dstFile, fileInfo, session.OverCallback(async delegate (Rpc.Methods.Name writeFile)
            {
                generator self = generator.self;
                Rpc.Session.CancelWait().AppendNotify(() => self.stop());
                FileStream file = null;
                try
                {
                    generator.lock_stop();
                    await generator.send_strand(ioStrand, delegate ()
                    {
                        try
                        {
                            file = File.OpenRead(path);
                        }
                        catch (Exception) { }
                    });
                    generator.unlock_stop();
                    if (null == file)
                    {
                        return false;
                    }
                    bool completed = await TransmitFile.Send(block, writeFile, file, sessions, ioStrand, progress);
                    if (!completed)
                    {
                        return false;
                    }
                    return true;
                }
                catch (generator.stop_exception)
                {
                    throw;
                }
                finally
                {
                    file?.Dispose();
                }
            }));
            if (!result.Success)
            {
                return false;
            }
            return result.result;
        }

        private async Task Action(string addr)
        {
            string ip = null;
            int port = 0;
            await generator.send_task(delegate ()
            {
                try
                {
                    if (null == addr)
                    {
                        addr = File.ReadAllText("./Address.txt");
                    }
                    string[] addr_ = addr.Split(':');
                    port = int.Parse(addr_[1]);
                    ip = addr_[0];
                }
                catch (Exception) { }
            });
            if (null == ip)
            {
                await generator.hold();
                return;
            }
            textBox_ip.Text = addr;
            while (true)
            {
                socket_tcp socket = new socket_tcp();
                Rpc.Methods methods = new Rpc.Methods();
                shared_strand sessStrand = new shared_strand();
                Rpc.Session session = new Rpc.Session(sessStrand, socket, methods);
                generator.children tokenChildren = new generator.children();
                try
                {
                    if (await socket.connect(ip, port))
                    {
                        await generator.send_task(() => File.WriteAllText("./Address.txt", addr));
                        bool tokenError = false;
                        int sessNum = (int)numericUpDown_sessions.Value;
                        long[] sessToken = new long[sessNum];
                        Rpc.Session[] sessions = new Rpc.Session[sessNum];
                        wait_group tokenWg = new wait_group(sessNum);
                        for (int i = 0; i < sessNum; i++)
                        {
                            int j = i;
                            tokenChildren.go(async delegate ()
                            {
                                await generator.sleep(1 + j);
                                for (int k = 0; k < 10; k++)
                                {
                                    socket_tcp socket_ = new socket_tcp();
                                    Rpc.Session session_ = new Rpc.Session(sessStrand, socket_, methods);
                                    try
                                    {
                                        if (await socket_.connect(ip, port))
                                        {
                                            session_.Start();
                                            Rpc.Result<long> token = await session_["IO.get@token"].Call<long>();
                                            if (token.Success)
                                            {
                                                sessToken[j] = token.result;
                                                sessions[j] = session_;
                                                tokenWg.done();
                                                await session_.Join();
                                                session.Close();
                                                return;
                                            }
                                        }
                                        await generator.sleep(100);
                                    }
                                    finally
                                    {
                                        session_.Close();
                                    }
                                }
                                tokenError = true;
                                tokenWg.done();
                            });
                        }
                        session.Start();
                        await tokenWg.wait();
                        if (tokenError)
                        {
                            continue;
                        }
                        expandChan.post(default);
                        generator.children children = new generator.children();
                        chan<float> progressChan = chan<float>.make(-1);
                        children.go(async delegate ()
                        {
                            progressBar1.Value = 0;
                            while (true)
                            {
                                float progress = await progressChan.receive();
                                progressBar1.Value = (int)(progressBar1.Maximum * Math.Min(1f, Math.Max(0f, progress)));
                            }
                        });
                        children.go(async delegate ()
                        {
                            generator.children enumChild = new generator.children();
                            while (true)
                            {
                                tuple<TreeNode, bool> tree = await expandChan.receive();
                                if (null == tree.value1)
                                {
                                    await enumChild.stop();
                                    treeView1.Nodes.Clear();
                                    enumChild.free_go(async delegate ()
                                    {
                                        var drives = await session["IO.GetDrives"].Call<string[]>();
                                        if (drives.Success)
                                        {
                                            for (int i = 0; i < drives.result.Length; i++)
                                            {
                                                string name = drives.result[i];
                                                TreeNode newNode = treeView1.Nodes.Add(name);
                                                newNode.Tag = new NodeTag { isFile = false, name = name };
                                                newNode.Nodes.Add($".\\").Tag = new NodeTag { isFile = false };
                                                await generator.sleep(1);
                                            }
                                        }
                                    });
                                }
                                else
                                {
                                    await StopEnumAction(enumChild, tree.value1);
                                    tree.value1.Nodes.Clear();
                                    if (tree.value2)
                                    {
                                        NodeTag tag = (NodeTag)tree.value1.Tag;
                                        enumChild.free_go(out tag.action, async delegate ()
                                        {
                                            await EnumTree(session, tree.value1);
                                        });
                                    }
                                    else
                                    {
                                        tree.value1.Nodes.Add($".\\");
                                    }
                                }
                            }
                        });
                        children.go(async delegate ()
                        {
                            generator.children testChild = new generator.children();
                            while (true)
                            {
                                bool test = await testChan.receive();
                                int block = (-4) & ((int)numericUpDown_block.Value + 3);
                                int interval = (int)numericUpDown_interval.Value;
                                if (test && block > 0)
                                {
                                    textBox_msg.Clear();
                                    long recvTotal = 0;
                                    long sendTotal = 0;
                                    testChild.go(async delegate ()
                                    {
                                        while (true)
                                        {
                                            long recvLast = recvTotal;
                                            long sendLast = sendTotal;
                                            long begin = system_tick.ms;
                                            await generator.sleep(1000);
                                            double time = 1000.0 / (system_tick.ms - begin);
                                            double recvSpeed = time * (recvTotal - recvLast) / (1 << 20);
                                            double sendSpeed = time * (sendTotal - sendLast) / (1 << 20);
                                            textBox_msg.Text = $"Recv:{recvSpeed:0.000}MB/S\r\nSend:{sendSpeed:0.000}MB/S";
                                        }
                                    });
                                    testChild.go(async delegate ()
                                    {
                                        try
                                        {
                                            await session["IO.set@block"].Call(block);
                                            generator.children ioChild = new generator.children();
                                            for (int i = 0; i < sessions.Length; i++)
                                            {
                                                await sessions[i]["IO.set@block"].Call(block);
                                            }
                                            byte[] data = new byte[block * 1024];
                                            while (true)
                                            {
                                                bool recvCancel = false;
                                                wait_group recvWg = new wait_group();
                                                for (int i = 0; i < sessions.Length; i++)
                                                {
                                                    int j = i;
                                                    ioChild.go(sessStrand, async delegate ()
                                                    {
                                                        await sessions[j]["IO.ReadTest"].Call(interval, sessions[j].OverCallback(async delegate ()
                                                        {
                                                            recvWg.add();
                                                            try
                                                            {
                                                                if (recvCancel)
                                                                {
                                                                    return;
                                                                }
                                                                Rpc.OverRecv[] recvs = Rpc.Session.OverRecv();
                                                                while (!recvs[0].Completed)
                                                                {
                                                                    await recvs[0].Recv(data, 0, data.Length);
                                                                    recvTotal += data.Length;
                                                                    await generator.try_yield();
                                                                }
                                                            }
                                                            finally
                                                            {
                                                                recvWg.done();
                                                            }
                                                        }));
                                                    });
                                                }
                                                await generator.sleep(10000);
                                                await ioChild.stop();
                                                recvCancel = true;
                                                await recvWg.wait();
                                                for (int i = 0; i < sessions.Length; i++)
                                                {
                                                    int j = i;
                                                    ioChild.go(sessStrand, async delegate ()
                                                    {
                                                        async_timer sendTimer = null;
                                                        chan<void_type> sendChan = null;
                                                        try
                                                        {
                                                            if (0 != interval)
                                                            {
                                                                sendTimer = new async_timer();
                                                                sendChan = chan<void_type>.make(1);
                                                                sendTimer.interval(Math.Abs(interval), sendChan.wrap_try_default(), interval < 0);
                                                            }
                                                            sessions[j].OverSend(long.MaxValue, async delegate (long offset, long length)
                                                            {
                                                                if (0 != interval)
                                                                {
                                                                    generator.unlock_stop();
                                                                    await sendChan.receive();
                                                                    generator.lock_stop();
                                                                }
                                                                await Rpc.Session.Send(data, 0, data.Length);
                                                                sendTotal += data.Length;
                                                            });
                                                            await sessions[j]["IO.WriteTest"].Call();
                                                        }
                                                        finally
                                                        {
                                                            sendTimer?.cancel();
                                                        }
                                                    });
                                                }
                                                await generator.sleep(10000);
                                                await ioChild.stop();
                                            }
                                        }
                                        finally
                                        {
                                            button_Load.Text = "传输";
                                        }
                                    });
                                }
                                else if (test && block == 0)
                                {
                                    textBox_msg.Clear();
                                    long tickSum = 0;
                                    int count = 0;
                                    testChild.go(async delegate ()
                                    {
                                        while (true)
                                        {
                                            long begin = system_tick.ms;
                                            await generator.sleep(1000);
                                            double time = 1000.0 / (system_tick.ms - begin);
                                            textBox_msg.Text = $"{(int)(time * count)}/S\r\n{tickSum / count}us/Call";
                                            tickSum = 0;
                                            count = 0;
                                        }
                                    });
                                    testChild.go(async delegate ()
                                    {
                                        try
                                        {
                                            generator.children ioChild = new generator.children();
                                            for (int i = 0; i < sessions.Length; i++)
                                            {
                                                int j = i;
                                                for (int k = 0; k < 10; k++)
                                                {
                                                    ioChild.go(sessStrand, async delegate ()
                                                    {
                                                        long begin = system_tick.us;
                                                        while ((await sessions[j]["IO.CallTest"].Call()).Success)
                                                        {
                                                            long end = system_tick.us;
                                                            tickSum += end - begin;
                                                            begin = end;
                                                            count++;
                                                        }
                                                    });
                                                }
                                            }
                                            await ioChild.wait_all();
                                        }
                                        finally
                                        {
                                            button_Load.Text = "传输";
                                        }
                                    });
                                }
                                else
                                {
                                    await testChild.stop();
                                }
                            }
                        });
                        children.go(async delegate ()
                        {
                            generator.children writeChild = new generator.children();
                            while (true)
                            {
                                tuple<string, string> tree = await fileChan.receive();
                                if (null != tree.value1)
                                {
                                    bool force = checkBox_Force.Checked;
                                    string file = tree.value1;
                                    writeChild.go(async delegate ()
                                    {
                                        try
                                        {
                                            bool ok = await ReadFileMT(session, sessToken, file, tree.value2, force, new shared_strand(), progressChan);
                                            if (!ok)
                                            {
                                                progressChan.post(0);
                                                textBox_msg.AppendText("~ ");
                                            }
                                            else
                                            {
                                                progressChan.post(1);
                                            }
                                            textBox_msg.AppendText($"--> {tree.value2}");
                                            textBox_msg.AppendText("\r\n");
                                        }
                                        finally
                                        {
                                            button_Load.Text = "传输";
                                        }
                                    });
                                }
                                else
                                {
                                    await writeChild.stop();
                                }
                            }
                        });
                        children.go(async delegate ()
                        {
                            generator.children writeChild = new generator.children();
                            while (true)
                            {
                                tuple<string, string> tree = await dirChan.receive();
                                if (null != tree.value1)
                                {
                                    int fileTotalCount = 0;
                                    long fileTotalSize = 0;
                                    textBox_msg.Clear();
                                    bool force = checkBox_Force.Checked;
                                    chan<long> postWrite = chan<long>.make(-1);
                                    chan<tuple<string, string, wait_group, long>> readChan = chan<tuple<string, string, wait_group, long>>.make(-1);
                                    chan<tuple<wait_group, string, DateTime[]>> dirWgChan = chan<tuple<wait_group, string, DateTime[]>>.make(-1);
                                    Func<string, string, Task> copyAction = null;
                                    copyAction = async delegate (string srcDir, string dstDir)
                                    {
                                        if (!force && Directory.Exists(dstDir))
                                        {
                                            await generator.send_control(this, () => MessageBox.Show($"文件夹已存在 {dstDir}"));
                                            return;
                                        }
                                        if (!await generator.send_task(delegate ()
                                        {
                                            try
                                            {
                                                Directory.CreateDirectory(dstDir);
                                                return true;
                                            }
                                            catch (Exception)
                                            {
                                                return false;
                                            }
                                        }))
                                        {
                                            await generator.send_control(this, () => MessageBox.Show($"文件夹新建失败 {dstDir}"));
                                            return;
                                        }
                                        wait_group dirWg = new wait_group();
                                        var files = await session["IO.GetFiles"].Call<string[]>(srcDir);
                                        if (files.Success)
                                        {
                                            for (int j = 0; j < files.result.Length; j++)
                                            {
                                                var sizeRes = await session["IO.FileSize"].Call<long>($"{srcDir}\\{files.result[j]}");
                                                if (sizeRes.Success)
                                                {
                                                    dirWg.add();
                                                    fileTotalCount++;
                                                    fileTotalSize += sizeRes.result;
                                                    await readChan.send(tuple.make($"{srcDir}\\{files.result[j]}", $"{dstDir}\\{files.result[j]}", dirWg, sizeRes.result));
                                                }
                                            }
                                        }
                                        var dirs = await session["IO.GetDirectories"].Call<string[]>(srcDir);
                                        if (dirs.Success)
                                        {
                                            for (int j = 0; j < dirs.result.Length; j++)
                                            {
                                                fileTotalSize++;
                                                textBox_msg.AppendText($"*--> {dstDir}\\{dirs.result[j]}");
                                                textBox_msg.AppendText("\r\n");
                                                await copyAction($"{srcDir}\\{dirs.result[j]}", $"{dstDir}\\{dirs.result[j]}");
                                                postWrite.post(1);
                                            }
                                        }
                                        var infos = await session["IO.DirectoryInfo"].Call<DateTime[]>(srcDir);
                                        if (infos.Success)
                                        {
                                            await dirWgChan.send(tuple.make(dirWg, dstDir, infos.result));
                                        }
                                    };
                                    writeChild.go(async delegate ()
                                    {
                                        progressChan.post(0);
                                        generator.children mtChild = new generator.children();
                                        try
                                        {
                                            int fileCount = 0;
                                            long fileSize = 0;
                                            mtChild.free_go(async delegate ()
                                            {
                                                while (true)
                                                {
                                                    fileSize += await postWrite.receive();
                                                }
                                            });
                                            mtChild.free_go(async delegate ()
                                            {
                                                try
                                                {
                                                    while (true)
                                                    {
                                                        this.Text = $"{SizeFormat(fileSize)}({fileCount})/{SizeFormat(fileTotalSize)}({fileTotalCount})";
                                                        progressChan.post(fileTotalSize + fileTotalCount > 0 ? (float)(fileSize + fileCount) / (float)(fileTotalSize + fileTotalCount) : 0);
                                                        await generator.sleep(1000);
                                                    }
                                                }
                                                finally
                                                {
                                                    textBox_msg.AppendText($"{SizeFormat(fileSize)}({fileCount})/{SizeFormat(fileTotalSize)}({fileTotalCount})");
                                                    textBox_msg.AppendText("\r\n");
                                                    progressChan.post(fileTotalSize + fileTotalCount > 0 ? (float)(fileSize + fileCount) / (float)(fileTotalSize + fileTotalCount) : 0);
                                                }
                                            });
                                            shared_strand ioStrand = new shared_strand();
                                            mtChild.go(async delegate ()
                                            {
                                                while (true)
                                                {
                                                    chan_recv_wrap<tuple<wait_group, string, DateTime[]>> res = await dirWgChan.receive();
                                                    if (res.state != chan_state.ok)
                                                    {
                                                        break;
                                                    }
                                                    await res.msg.value1.wait();
                                                    await generator.send_strand(ioStrand, delegate ()
                                                    {
                                                        try
                                                        {
                                                            Directory.SetLastWriteTime(res.msg.value2, res.msg.value3[0]);
                                                            Directory.SetCreationTime(res.msg.value2, res.msg.value3[1]);
                                                            Directory.SetLastAccessTime(res.msg.value2, res.msg.value3[2]);
                                                        }
                                                        catch (Exception) { }
                                                    });
                                                }
                                            });
                                            int block = Math.Max(1, (-4) & ((int)numericUpDown_block.Value + 3));
                                            await session["IO.set@block"].Call(block);
                                            for (int i = 0; i < sessions.Length; i++)
                                            {
                                                int j = i;
                                                mtChild.go(async delegate ()
                                                {
                                                    await sessions[j]["IO.set@block"].Call(block);
                                                    while (true)
                                                    {
                                                        chan_recv_wrap<tuple<string, string, wait_group, long>> res = await readChan.receive();
                                                        if (res.state != chan_state.ok)
                                                        {
                                                            break;
                                                        }
                                                        bool ok = await ReadFile(sessions[j], block, res.msg.value4, res.msg.value1, res.msg.value2, force, ioStrand, postWrite);
                                                        if (!ok)
                                                        {
                                                            textBox_msg.AppendText("~ ");
                                                        }
                                                        textBox_msg.AppendText($"--> {res.msg.value2}");
                                                        textBox_msg.AppendText("\r\n");
                                                        fileCount++;
                                                        res.msg.value3.done();
                                                    }
                                                });
                                            }
                                            await copyAction(tree.value1, $"{tree.value2}\\{Path.GetFileName(tree.value1)}");
                                            readChan.close();
                                            dirWgChan.close();
                                            await mtChild.wait_all(false);
                                            textBox_msg.AppendText("已完成\r\n");
                                        }
                                        finally
                                        {
                                            generator.lock_stop();
                                            await mtChild.stop();
                                            button_Load.Text = "传输";
                                            this.Text = "FileView";
                                            generator.unlock_stop();
                                        }
                                    });
                                }
                                else
                                {
                                    await writeChild.stop();
                                }
                            }
                        });
                        children.go(async delegate ()
                        {
                            generator.children writeChild = new generator.children();
                            while (true)
                            {
                                tuple<TreeNode, string[]> copys = await dragChan.receive();
                                if (null != copys.value1)
                                {
                                    textBox_msg.Clear();
                                    bool force = checkBox_Force.Checked;
                                    shared_strand ioStrand = new shared_strand();
                                    int fileTotalCount = 0;
                                    long fileTotalSize = 0;
                                    chan<long> postWrite = chan<long>.make(-1);
                                    chan<tuple<TreeNode, IoBase>> writeChan = chan<tuple<TreeNode, IoBase>>.make(-1);
                                    Func<TreeNode, string[], string, wait_group, wait_group, Task> copyAction = null;
                                    copyAction = async delegate (TreeNode root, string[] srcDir, string dstDir, wait_group notifyIo, wait_group waitCreateDir)
                                    {
                                        if (root.IsExpanded)
                                        {
                                            expandChan.post(tuple.make(root, true));
                                        }
                                        for (int i = 0; i < srcDir.Length; i++)
                                        {
                                            string name = Path.GetFileName(srcDir[i]);
                                            if (File.Exists(srcDir[i]))
                                            {
                                                long size = 0;
                                                if (!await generator.send_strand(ioStrand, delegate ()
                                                {
                                                    try
                                                    {
                                                        size = new FileInfo(srcDir[i]).Length;
                                                        return true;
                                                    }
                                                    catch (Exception)
                                                    {
                                                        return false;
                                                    }
                                                }))
                                                {
                                                    notifyIo.done();
                                                    continue;
                                                }
                                                fileTotalSize += size;
                                                fileTotalCount++;
                                                await writeChan.send(tuple.make(root, (IoBase)new CreateFile { srcFile = srcDir[i], dstFile = $"{dstDir}\\{name}", waitCreateDir = waitCreateDir, notifyIo = notifyIo }));
                                            }
                                            else if (Directory.Exists(srcDir[i]))
                                            {
                                                textBox_msg.AppendText($"*--> {srcDir[i]}");
                                                textBox_msg.AppendText("\r\n");
                                                fileTotalSize++;
                                                TreeNode newNode = root.Nodes.Add(name);
                                                newNode.Tag = new NodeTag { isFile = false, name = name };
                                                string[] files = await generator.send_task(() => Directory.GetFiles(srcDir[i]));
                                                string[] dirs = await generator.send_task(() => Directory.GetDirectories(srcDir[i]));
                                                wait_group createDir = new wait_group(1, true);
                                                wait_group notifyIo2 = new wait_group(files.Length + dirs.Length, true);
                                                await writeChan.send(tuple.make(root, (IoBase)new CreateDir { dir = $"{dstDir}\\{name}", waitCreateDir = waitCreateDir, notifyCreateDir = createDir, notifyIo = notifyIo }));
                                                await copyAction(newNode, files, $"{dstDir}\\{name}", notifyIo2, createDir);
                                                await copyAction(newNode, dirs, $"{dstDir}\\{name}", notifyIo2, createDir);
                                                if (root.IsExpanded)
                                                {
                                                    expandChan.post(tuple.make(root, true));
                                                }
                                                DateTime[] dirTime = await generator.send_strand(ioStrand, delegate ()
                                                {
                                                    return new DateTime[]
                                                    {
                                                        Directory.GetLastWriteTime(srcDir[i]),
                                                        Directory.GetCreationTime(srcDir[i]),
                                                        Directory.GetLastAccessTime(srcDir[i]),
                                                    };
                                                });
                                                await writeChan.send(tuple.make(root, (IoBase)new SetDirTime { dir = $"{dstDir}\\{name}", time = dirTime, waitIo = notifyIo2 }));
                                            }
                                            else
                                            {
                                                notifyIo.done();
                                            }
                                        }
                                    };
                                    writeChild.go(async delegate ()
                                    {
                                        progressChan.post(0);
                                        generator.children mtChild = new generator.children();
                                        try
                                        {
                                            int block = Math.Max(1, (-4) & ((int)numericUpDown_block.Value + 3));
                                            await session["IO.set@block"].Call(block);
                                            if (copys.value2.Length == 1 && File.Exists(copys.value2[0]))
                                            {
                                                TreeNode root = copys.value1;
                                                string[] srcDir = copys.value2;
                                                builder.Clear();
                                                GetFullPath(builder, copys.value1);
                                                string dstDir = builder.ToString();
                                                string name = Path.GetFileName(srcDir[0]);
                                                bool ok = await WriteFileMT(session, block, sessToken, sessions, srcDir[0], $"{dstDir}\\{name}", force, ioStrand, progressChan);
                                                if (!ok)
                                                {
                                                    progressChan.post(0);
                                                    textBox_msg.AppendText("~  ");
                                                }
                                                else
                                                {
                                                    progressChan.post(1f);
                                                }
                                                textBox_msg.AppendText($"--> {srcDir[0]}");
                                                textBox_msg.AppendText("\r\n");
                                                if (root.IsExpanded)
                                                {
                                                    expandChan.post(tuple.make(root, true));
                                                }
                                            }
                                            else
                                            {
                                                int fileCount = 0;
                                                long fileSize = 0;
                                                mtChild.free_go(async delegate ()
                                                {
                                                    while (true)
                                                    {
                                                        fileSize += await postWrite.receive();
                                                    }
                                                });
                                                mtChild.free_go(async delegate ()
                                                {
                                                    try
                                                    {
                                                        while (true)
                                                        {
                                                            this.Text = $"{SizeFormat(fileSize)}({fileCount})/{SizeFormat(fileTotalSize)}({fileTotalCount})";
                                                            progressChan.post(fileTotalSize + fileTotalCount > 0 ? (float)(fileSize + fileCount) / (float)(fileTotalSize + fileTotalCount) : 0);
                                                            await generator.sleep(100);
                                                        }
                                                    }
                                                    finally
                                                    {
                                                        textBox_msg.AppendText($"{SizeFormat(fileSize)}({fileCount})/{SizeFormat(fileTotalSize)}({fileTotalCount})");
                                                        textBox_msg.AppendText("\r\n");
                                                        progressChan.post(fileTotalSize + fileTotalCount > 0 ? (float)(fileSize + fileCount) / (float)(fileTotalSize + fileTotalCount) : 0);
                                                    }
                                                });
                                                for (int i = 0; i < sessions.Length; i++)
                                                {
                                                    int j = i;
                                                    mtChild.go(async delegate ()
                                                    {
                                                        await sessions[j]["IO.set@block"].Call(block);
                                                        while (true)
                                                        {
                                                            chan_recv_wrap<tuple<TreeNode, IoBase>> res = await writeChan.receive();
                                                            if (res.state != chan_state.ok)
                                                            {
                                                                break;
                                                            }
                                                            if (res.msg.value2 is CreateFile)
                                                            {
                                                                CreateFile file = (CreateFile)res.msg.value2;
                                                                await file.waitCreateDir.wait();
                                                                bool ok = await WriteFile(sessions[j], block, file.srcFile, file.dstFile, force, ioStrand, postWrite);
                                                                if (!ok)
                                                                {
                                                                    progressChan.post(0);
                                                                    textBox_msg.AppendText("~  ");
                                                                }
                                                                textBox_msg.AppendText($"--> {file.srcFile}");
                                                                textBox_msg.AppendText("\r\n");
                                                                if (res.msg.value1.IsExpanded)
                                                                {
                                                                    expandChan.post(tuple.make(res.msg.value1, true));
                                                                }
                                                                fileCount++;
                                                                file.notifyIo.done();
                                                            }
                                                            else if (res.msg.value2 is CreateDir)
                                                            {
                                                                CreateDir dir = (CreateDir)res.msg.value2;
                                                                await dir.waitCreateDir.wait();
                                                                var createRes = await session["IO.CreateDirectory"].Call(dir.dir);
                                                                postWrite.post(1);
                                                                if (!createRes.Success)
                                                                {
                                                                    textBox_msg.AppendText($"~  {dir.dir}");
                                                                }
                                                                dir.notifyCreateDir.done();
                                                                dir.notifyIo.done();
                                                            }
                                                            else if (res.msg.value2 is SetDirTime)
                                                            {
                                                                SetDirTime time = (SetDirTime)res.msg.value2;
                                                                await time.waitIo.wait();
                                                                await session["IO.SetDirectoryInfo"].Call(time.dir, time.time);
                                                            }
                                                        }
                                                    });
                                                }
                                                builder.Clear();
                                                GetFullPath(builder, copys.value1);
                                                string dstDir = builder.ToString();
                                                await copyAction(copys.value1, copys.value2, dstDir, new wait_group(0, true), new wait_group(0, true));
                                                writeChan.close();
                                                await mtChild.wait_all(false);
                                            }
                                            textBox_msg.AppendText("已完成\r\n");
                                        }
                                        finally
                                        {
                                            generator.lock_stop();
                                            await mtChild.stop();
                                            button_Load.Text = "传输";
                                            this.Text = "FileView";
                                            generator.unlock_stop();
                                        }
                                    });
                                }
                                else
                                {
                                    await writeChild.stop();
                                }
                            }
                        });
                        await session.Join();
                        await children.stop();
                        treeView1.Nodes.Clear();
                    }
                }
                finally
                {
                    generator.lock_stop();
                    await tokenChildren.stop();
                    session.Close();
                    generator.unlock_stop();
                }
                await generator.sleep(1000);
            }
        }

        private void treeView1_BeforeExpand(object sender, TreeViewCancelEventArgs e)
        {
            expandChan.post(tuple.make(e.Node, true));
        }

        private void treeView1_BeforeCollapse(object sender, TreeViewCancelEventArgs e)
        {
            expandChan.post(tuple.make(e.Node, false));
        }

        private void button_Load_Click(object sender, EventArgs e)
        {
            if (button_Load.Text == "传输")
            {
                if (checkBox_Test.Checked)
                {
                    button_Load.Text = "停止";
                    testChan.post(true);
                }
                else
                {
                    if (null == treeView1.SelectedNode)
                    {
                        return;
                    }
                    StringBuilder builder = new StringBuilder(256);
                    GetFullPath(builder, treeView1.SelectedNode);
                    string srcFile = builder.ToString();
                    string dstFile = null;
                    if (((NodeTag)treeView1.SelectedNode.Tag).isFile)
                    {
                        SaveFileDialog saveFileDialog = new SaveFileDialog();
                        saveFileDialog.FileName = Path.GetFileName(srcFile);
                        if (saveFileDialog.ShowDialog(this) == DialogResult.OK)
                        {
                            dstFile = saveFileDialog.FileName;
                        }
                    }
                    else
                    {
                        FolderBrowserDialog saveFileDialog = new FolderBrowserDialog();
                        saveFileDialog.SelectedPath = Path.GetFileName(srcFile);
                        if (saveFileDialog.ShowDialog(this) == DialogResult.OK)
                        {
                            dstFile = saveFileDialog.SelectedPath;
                        }
                    }
                    if (null != dstFile && null != treeView1.SelectedNode.Tag)
                    {
                        button_Load.Text = "停止";
                        if (((NodeTag)treeView1.SelectedNode.Tag).isFile)
                        {
                            fileChan.post(tuple.make(srcFile, dstFile));
                        }
                        else
                        {
                            dirChan.post(tuple.make(srcFile, dstFile));
                        }
                    }
                }
            }
            else if (button_Load.Text == "停止")
            {
                fileChan.post(default);
                dirChan.post(default);
                dragChan.post(default);
                testChan.post(false);
            }
        }

        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            StringBuilder builder = new StringBuilder(256);
            GetFullPath(builder, treeView1.SelectedNode);
            textBox_file.Text = builder.ToString();
        }

        private void treeView1_DragEnter(object sender, DragEventArgs e)
        {
            if (button_Load.Text != "传输")
            {
                return;
            }
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Copy;
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }

        private void treeView1_DragDrop(object sender, DragEventArgs e)
        {
            if (button_Load.Text != "传输")
            {
                return;
            }
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                TreeNode node = treeView1.GetNodeAt(treeView1.PointToClient(new Point { X = e.X, Y = e.Y }));
                if (null != node)
                {
                    NodeTag tag = (NodeTag)node.Tag;
                    if (!tag.isFile)
                    {
                        treeView1.SelectedNode = node;
                        button_Load.Text = "停止";
                        string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
                        dragChan.post(tuple.make(node, files));
                    }
                }
            }
        }

        private void treeView1_DragOver(object sender, DragEventArgs e)
        {
            if (button_Load.Text != "传输")
            {
                return;
            }
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                TreeNode node = treeView1.GetNodeAt(treeView1.PointToClient(new Point { X = e.X, Y = e.Y }));
                if (null != node)
                {
                    NodeTag tag = (NodeTag)node.Tag;
                    if (!tag.isFile)
                    {
                        e.Effect = DragDropEffects.Copy;
                        treeView1.SelectedNode = node;
                        return;
                    }
                }
            }
            e.Effect = DragDropEffects.None;
        }
    }
}
